"""
熔断器中间件
提供服务容错和故障隔离功能
"""
import asyncio
import time
import threading
from enum import Enum
from typing import Optional, Callable, Any, Dict
from dataclasses import dataclass, field
from loguru import logger

from utils.logger import Logger


class CircuitState(Enum):
    """熔断器状态"""
    CLOSED = "closed"      # 关闭状态 - 正常处理请求
    OPEN = "open"          # 开启状态 - 拒绝请求，快速失败
    HALF_OPEN = "half_open"  # 半开状态 - 允许部分请求测试服务是否恢复


@dataclass
class CircuitBreakerConfig:
    """熔断器配置"""
    failure_threshold: int = 5          # 失败阈值
    recovery_timeout: int = 60          # 恢复超时时间（秒）
    success_threshold: int = 3        # 成功阈值（半开状态下）
    expected_exception: type = Exception  # 预期的异常类型
    timeout_duration: int = 30          # 请求超时时间（秒）
    
    def __post_init__(self):
        if self.failure_threshold <= 0:
            raise ValueError("失败阈值必须大于0")
        if self.recovery_timeout <= 0:
            raise ValueError("恢复超时时间必须大于0")
        if self.success_threshold <= 0:
            raise ValueError("成功阈值必须大于0")


@dataclass
class CircuitMetrics:
    """熔断器指标"""
    total_requests: int = 0
    successful_requests: int = 0
    failed_requests: int = 0
    consecutive_failures: int = 0
    consecutive_successes: int = 0
    last_failure_time: Optional[float] = None
    last_success_time: Optional[float] = None
    state_transitions: int = 0
    
    def success_rate(self) -> float:
        """计算成功率"""
        if self.total_requests == 0:
            return 1.0
        return self.successful_requests / self.total_requests
    
    def failure_rate(self) -> float:
        """计算失败率"""
        if self.total_requests == 0:
            return 0.0
        return self.failed_requests / self.total_requests


class CircuitBreaker:
    """熔断器"""
    
    def __init__(self, config: Optional[CircuitBreakerConfig] = None, name: str = "default"):
        self.config = config or CircuitBreakerConfig()
        self.name = name
        self.logger = Logger.get_logger(agent_name=f"circuit_breaker_{name}")
        
        # 状态管理
        self._state = CircuitState.CLOSED
        self._state_lock = threading.RLock()
        self._last_state_change_time = time.time()
        
        # 指标统计
        self.metrics = CircuitMetrics()
        self._metrics_lock = threading.Lock()
        
        # 半开状态下的请求计数
        self._half_open_requests = 0
        self._half_open_successes = 0
        
        self.logger.info(f"熔断器初始化完成: {name}, 配置: {self.config}")
    
    @property
    def state(self) -> CircuitState:
        """获取当前状态"""
        with self._state_lock:
            return self._state
    
    @property
    def state_duration(self) -> float:
        """获取当前状态持续时间"""
        return time.time() - self._last_state_change_time
    
    async def call(self, func: Callable, *args, **kwargs) -> Any:
        """
        通过熔断器调用函数
        
        Args:
            func: 要调用的函数
            *args, **kwargs: 函数参数
            
        Returns:
            函数返回值
            
        Raises:
            Exception: 当熔断器开启或服务调用失败时
        """
        # 检查是否应该拒绝请求
        if not self._should_allow_request():
            self.logger.warning(f"熔断器拒绝请求: {self.name}, 状态: {self._state.value}")
            raise Exception(f"Circuit breaker is {self._state.value}")
        
        try:
            # 执行函数调用
            result = await self._execute_call(func, *args, **kwargs)
            self._on_success()
            return result
            
        except Exception as e:
            self._on_failure(e)
            raise
    
    def _should_allow_request(self) -> bool:
        """检查是否应该允许请求"""
        with self._state_lock:
            current_time = time.time()
            
            if self._state == CircuitState.CLOSED:
                return True
            
            elif self._state == CircuitState.OPEN:
                # 检查是否到了恢复时间
                if current_time - self._last_state_change_time >= self.config.recovery_timeout:
                    self._transition_to_half_open()
                    return True
                return False
            
            elif self._state == CircuitState.HALF_OPEN:
                # 在半开状态下，允许有限数量的请求
                return self._half_open_requests < self.config.success_threshold
            
            return False
    
    async def _execute_call(self, func: Callable, *args, **kwargs) -> Any:
        """执行函数调用"""
        # 设置超时
        if asyncio.iscoroutinefunction(func):
            # 异步函数
            return await asyncio.wait_for(
                func(*args, **kwargs), 
                timeout=self.config.timeout_duration
            )
        else:
            # 同步函数，在线程池中执行
            loop = asyncio.get_event_loop()
            return await asyncio.wait_for(
                loop.run_in_executor(None, func, *args, **kwargs),
                timeout=self.config.timeout_duration
            )
    
    def _on_success(self):
        """处理成功调用"""
        current_time = time.time()
        
        with self._metrics_lock:
            self.metrics.total_requests += 1
            self.metrics.successful_requests += 1
            self.metrics.consecutive_successes += 1
            self.metrics.consecutive_failures = 0
            self.metrics.last_success_time = current_time
        
        with self._state_lock:
            if self._state == CircuitState.HALF_OPEN:
                self._half_open_successes += 1
                
                # 如果连续成功次数达到阈值，关闭熔断器
                if self._half_open_successes >= self.config.success_threshold:
                    self._transition_to_closed()
                    self.logger.info(f"熔断器关闭: {self.name}, 服务恢复正常")
        
        self.logger.debug(f"调用成功: {self.name}, 状态: {self._state.value}")
    
    def _on_failure(self, exception: Exception):
        """处理失败调用"""
        current_time = time.time()
        
        # 检查是否是预期的异常类型
        if not isinstance(exception, self.config.expected_exception):
            self.logger.warning(f"非预期异常类型: {type(exception).__name__}, 忽略")
            return
        
        with self._metrics_lock:
            self.metrics.total_requests += 1
            self.metrics.failed_requests += 1
            self.metrics.consecutive_failures += 1
            self.metrics.consecutive_successes = 0
            self.metrics.last_failure_time = current_time
        
        with self._state_lock:
            if self._state == CircuitState.HALF_OPEN:
                # 在半开状态下失败，重新开启熔断器
                self._transition_to_open()
                self.logger.warning(f"熔断器重新开启: {self.name}, 服务仍然不可用")
                
            elif self._state == CircuitState.CLOSED:
                # 检查是否达到失败阈值
                if self.metrics.consecutive_failures >= self.config.failure_threshold:
                    self._transition_to_open()
                    self.logger.warning(f"熔断器开启: {self.name}, 连续失败次数: {self.metrics.consecutive_failures}")
        
        self.logger.debug(f"调用失败: {self.name}, 异常: {str(exception)}, 状态: {self._state.value}")
    
    def _transition_to_open(self):
        """转换到开启状态"""
        with self._state_lock:
            if self._state != CircuitState.OPEN:
                self._state = CircuitState.OPEN
                self._last_state_change_time = time.time()
                self._half_open_requests = 0
                self._half_open_successes = 0
                
                with self._metrics_lock:
                    self.metrics.state_transitions += 1
    
    def _transition_to_half_open(self):
        """转换到半开状态"""
        with self._state_lock:
            if self._state != CircuitState.HALF_OPEN:
                self._state = CircuitState.HALF_OPEN
                self._last_state_change_time = time.time()
                self._half_open_requests = 0
                self._half_open_successes = 0
                
                with self._metrics_lock:
                    self.metrics.state_transitions += 1
    
    def _transition_to_closed(self):
        """转换到关闭状态"""
        with self._state_lock:
            if self._state != CircuitState.CLOSED:
                self._state = CircuitState.CLOSED
                self._last_state_change_time = time.time()
                
                with self._metrics_lock:
                    self.metrics.state_transitions += 1
    
    def get_stats(self) -> Dict[str, Any]:
        """获取熔断器统计信息"""
        with self._metrics_lock:
            metrics = CircuitMetrics(
                total_requests=self.metrics.total_requests,
                successful_requests=self.metrics.successful_requests,
                failed_requests=self.metrics.failed_requests,
                consecutive_failures=self.metrics.consecutive_failures,
                consecutive_successes=self.metrics.consecutive_successes,
                last_failure_time=self.metrics.last_failure_time,
                last_success_time=self.metrics.last_success_time,
                state_transitions=self.metrics.state_transitions
            )
        
        return {
            "name": self.name,
            "state": self._state.value,
            "state_duration": self.state_duration,
            "metrics": {
                "total_requests": metrics.total_requests,
                "successful_requests": metrics.successful_requests,
                "failed_requests": metrics.failed_requests,
                "success_rate": metrics.success_rate(),
                "failure_rate": metrics.failure_rate(),
                "consecutive_failures": metrics.consecutive_failures,
                "consecutive_successes": metrics.consecutive_successes,
                "last_failure_time": metrics.last_failure_time,
                "last_success_time": metrics.last_success_time,
                "state_transitions": metrics.state_transitions
            },
            "config": {
                "failure_threshold": self.config.failure_threshold,
                "recovery_timeout": self.config.recovery_timeout,
                "success_threshold": self.config.success_threshold,
                "timeout_duration": self.config.timeout_duration
            }
        }
    
    def reset(self):
        """重置熔断器状态"""
        with self._state_lock:
            self._state = CircuitState.CLOSED
            self._last_state_change_time = time.time()
            self._half_open_requests = 0
            self._half_open_successes = 0
        
        with self._metrics_lock:
            self.metrics = CircuitMetrics()
        
        self.logger.info(f"熔断器已重置: {self.name}")


class CircuitBreakerManager:
    """熔断器管理器"""
    
    def __init__(self):
        self.circuit_breakers: Dict[str, CircuitBreaker] = {}
        self.logger = Logger.get_logger(agent_name="circuit_breaker_manager")
    
    def get_circuit_breaker(self, name: str, config: Optional[CircuitBreakerConfig] = None) -> CircuitBreaker:
        """获取或创建熔断器"""
        if name not in self.circuit_breakers:
            self.circuit_breakers[name] = CircuitBreaker(config, name)
            self.logger.info(f"创建熔断器: {name}")
        
        return self.circuit_breakers[name]
    
    def remove_circuit_breaker(self, name: str):
        """移除熔断器"""
        if name in self.circuit_breakers:
            del self.circuit_breakers[name]
            self.logger.info(f"移除熔断器: {name}")
    
    def get_all_stats(self) -> Dict[str, Any]:
        """获取所有熔断器的统计信息"""
        stats = {}
        for name, breaker in self.circuit_breakers.items():
            stats[name] = breaker.get_stats()
        
        return {
            "total_breakers": len(self.circuit_breakers),
            "breakers": stats
        }
    
    def reset_all(self):
        """重置所有熔断器"""
        for name, breaker in self.circuit_breakers.items():
            breaker.reset()
        
        self.logger.info("所有熔断器已重置")
    
    def cleanup_inactive(self, max_inactive_time: int = 3600):
        """清理长时间不活跃的熔断器"""
        current_time = time.time()
        inactive_names = []
        
        for name, breaker in self.circuit_breakers.items():
            last_activity = max(
                breaker.metrics.last_success_time or 0,
                breaker.metrics.last_failure_time or 0
            )
            
            if current_time - last_activity > max_inactive_time:
                inactive_names.append(name)
        
        for name in inactive_names:
            self.remove_circuit_breaker(name)
        
        if inactive_names:
            self.logger.info(f"清理不活跃熔断器: {inactive_names}")